package com.geek.bulk.interceptor;

import org.eclipse.microprofile.faulttolerance.Asynchronous;
import org.eclipse.microprofile.faulttolerance.exceptions.FaultToleranceDefinitionException;

import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;


@Interceptor
public class AsynchronousInterceptor extends AnnotatedInterceptor<Asynchronous> {

    private final ExecutorService executorService = ForkJoinPool.commonPool();

    @Override
    protected Object execute(InvocationContext context, Asynchronous asynchronous) throws Exception {
        Method method = context.getMethod();
        if (validateMethod(context.getMethod(), Future.class)) {
            return executeFeture(context);
        }
        if (validateMethod(context.getMethod(), CompletableFuture.class)) {
            return executeCompletableFuture(context);
        }
        throw new FaultToleranceDefinitionException("The return type of @Asynchronous method must be " +
                "java.util.concurrent.Future or java.util.concurrent.CompletableFuture!");
    }

    private Object executeCompletableFuture(InvocationContext context) throws Exception {
        CompletableFuture completableFuture = new CompletableFuture();
        Object proceed = context.proceed();
        completableFuture.complete(proceed);
        return completableFuture;
    }

    private Object executeFeture(InvocationContext context) {
        return executorService.submit(context::proceed);
    }

    private boolean validateMethod(Method method, Class<?> exceptReturnType) {
        Class<?> returnType = method.getReturnType();
        return exceptReturnType.isAssignableFrom(returnType);
    }
}
